﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EONFULLSIN2.Core.Utils {
    public static class MathUtils {
        /// <summary>
        /// 十六进制 转 二进制 
        /// </summary>
        /// <param name="hexString"></param>
        /// <returns></returns>
        public static string HexString2BinString(string hexString) {
            var int32 = Convert.ToInt32(hexString, 16);
            return Convert.ToString(int32, 2);
        }

        /// <summary>
        /// byte数组转化为16进制列表
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static List<string> ByteArrayToHexList(byte[] data) {
            List<string> list = new List<string>();
            StringBuilder sb = new StringBuilder(data.Length * 3);
            foreach (byte b in data) {
                list.Add(Convert.ToString(b, 16).PadLeft(2, '0'));
            }
            return list;
        }

        /// <summary>
        ///  byte数组转化为16进制字符串
        /// </summary>
        /// <param name="data"></param>
        /// <param name="symbol"></param>
        /// <returns></returns>
        public static string ByteArrayToHexString(byte[] data, string symbol = "") {
            StringBuilder sb = new StringBuilder(data.Length * 3);
            foreach (byte b in data) {
                sb.Append(symbol + Convert.ToString(b, 16).PadLeft(2, '0'));
            }
            return sb.ToString().ToUpper();
        }

        /// <summary>
        /// 16进制字符串转化为byte数组
        /// </summary>
        /// <param name="hexString"></param>
        /// <returns></returns>
        public static byte[] HexStringToByteArray(string hexString) {
            string code = "0123456789ABCDEF";
            int len = hexString.Length / 2;
            byte[] result = new byte[len];
            char[] achar = hexString.ToCharArray();
            for (int i = 0; i < len; i++) {
                int pos = i * 2;
                result[i] = (byte)(((byte)code.IndexOf(achar[pos])) << 4 | ((byte)code.IndexOf(achar[pos + 1])));
            }
            return result;
        }

        /// <summary>
        /// 16进制字符串转化为byte数组2
        /// </summary>
        /// <param name="hexString"></param>
        /// <returns></returns>
        public static byte[] HexStringToByteArray2(string hexString) {
            //将16进制秘钥转成字节数组
            var byteArray = new byte[hexString.Length / 2];
            for (var x = 0; x < byteArray.Length; x++) {
                var i = Convert.ToInt32(hexString.Substring(x * 2, 2), 16);
                byteArray[x] = (byte)i;
            }
            return byteArray;
        }

        public static string HexStringAdd(string hexString) {
            if (hexString.Length == 0 || hexString.Length % 2 != 0) return null;
            int sum = 0;
            for (int i = 0; i < hexString.Length; i += 2) {
                sum += Convert.ToInt32(hexString.Substring(i, 2), 16);
            }
            return sum.ToString("X");
        }

        /// <summary>
        /// byte数组中取int数值，本方法适用于(低位在前，高位在后)的顺序，和和IntToBytes（）配套使用
        /// </summary>
        /// <param name="src">byte数组</param>
        /// <param name="offset">从数组的第offset位开始</param>
        /// <returns></returns>
        public static int BytesToInt(byte[] src, int offset = 0) {
            int value;
            value = (int)((src[offset] & 0xFF)
                    | ((src[offset + 1] & 0xFF) << 8)
                    | ((src[offset + 2] & 0xFF) << 16)
                    | ((src[offset + 3] & 0xFF) << 24));
            return value;
        }

        /// <summary>
        /// byte数组中取int数值，本方法适用于(低位在后，高位在前)的顺序。和IntToBytes2（）配套使用
        /// </summary>
        /// <param name="src">byte数组</param>
        /// <param name="offset">从数组的第offset位开始</param>
        /// <returns></returns>
        public static int BytesToInt2(byte[] src, int offset = 0) {
            int value;
            value = (int)(((src[offset] & 0xFF) << 24)
                    | ((src[offset + 1] & 0xFF) << 16)
                    | ((src[offset + 2] & 0xFF) << 8)
                    | (src[offset + 3] & 0xFF));
            return value;
        }

        /// <summary>
        /// 将int数值转换为占四个字节的byte数组，本方法适用于(低位在前，高位在后)的顺序。 和BytesToInt（）配套使用
        /// </summary>
        /// <param name="value">要转换的int值</param>
        /// <returns></returns>
        public static byte[] IntToBytes(int value) {
            byte[] src = new byte[4];
            src[3] = (byte)((value >> 24) & 0xFF);
            src[2] = (byte)((value >> 16) & 0xFF);
            src[1] = (byte)((value >> 8) & 0xFF);
            src[0] = (byte)(value & 0xFF);
            return src;
        }

        /// <summary>
        /// 将int数值转换为占四个字节的byte数组，本方法适用于(高位在前，低位在后)的顺序。  和BytesToInt2（）配套使用
        /// </summary>
        /// <param name="value">要转换的int值</param>
        /// <returns></returns>
        public static byte[] IntToBytes2(int value) {
            byte[] src = new byte[4];
            src[0] = (byte)((value >> 24) & 0xFF);
            src[1] = (byte)((value >> 16) & 0xFF);
            src[2] = (byte)((value >> 8) & 0xFF);
            src[3] = (byte)(value & 0xFF);
            return src;
        }

        /// <summary>
        /// 字符串转化为16进制
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string StringToHex(string str) {
            StringBuilder sb = new StringBuilder();
            byte[] byStr = Encoding.Default.GetBytes(str);
            for (int i = 0; i < byStr.Length; i++) {
                sb.Append(Convert.ToString(byStr[i], 16));
            }
            return sb.ToString().ToUpper();
        }

        public static string StringToHex2(string str) {
            StringBuilder sb = new StringBuilder();
            byte[] byStr = Encoding.Default.GetBytes(str);
            for (int i = 0; i < byStr.Length; i++) {
                sb.Append(Convert.ToString(byStr[i], 16) + " ");
            }
            return sb.ToString().ToUpper().Substring(0, sb.Length - 1);
        }

        /// <summary>
        /// 十六进制转化为字符串(ASCII表)
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string HexToString(string str) {
            return Encoding.ASCII.GetString(HexStringToByteArray(str));
        }


        /// <summary>
        /// 十六进数组制转化为字符串(ASCII表)
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string HexArrToString(string[] str) {
            string res = string.Empty;
            foreach (var hexStr in str) {
                res += Encoding.ASCII.GetString(HexStringToByteArray(hexStr));
            }
            return res;
        }

        /// <summary>
        /// 十六进制数组转化为字符串
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static string HexByteArrayToString(byte[] data) {
            try {
                StringBuilder sb = new StringBuilder(data.Length * 3);
                foreach (byte b in data)
                    sb.Append(Convert.ToString(b, 16).PadLeft(2, '0'));
                return sb.ToString().ToUpper();
            } catch (Exception) {
                return "";
            }
        }

        public static string Decimal2Binary(string num) {

            string a, c = null, c1 = null;
            int b, b1;
            a = num;
            if (a.IndexOf('.') > 0) {
                string dot = a.Split('.')[1];
                double z1 = Convert.ToInt32(dot) * Math.Pow(10, -dot.Length);
                for (int i = 0; i < 6; i++)    //保留了六位二进制小数
                {
                    b1 = (int)(z1 * 2);
                    z1 = z1 * 2 - b1;
                    c1 = c1 + Convert.ToString(b1);
                }
                c1 = "." + c1;
            }
            string inum = a.Split('.')[0];
            int z = Convert.ToInt32(inum);
            for (; z != 0;) {
                b = z % 2;
                z = z / 2;
                c = Convert.ToString(b) + c;
            }

            return $"{c}{c1}";
        }

        /// <summary>
        /// 异或校验
        /// </summary>
        /// <param name="cmdString"></param>
        /// <returns></returns>
        public static string CRC(string cmdString) {
            //CRC寄存器
            int CRCCode = 0;
            //将字符串拆分成为16进制字节数据然后两位两位进行异或校验
            for (int i = 1; i < cmdString.Length / 2; i++) {
                string cmdHex = cmdString.Substring(i * 2, 2);
                if (i == 1) {
                    string cmdPrvHex = cmdString.Substring((i - 1) * 2, 2);
                    CRCCode = (byte)Convert.ToInt32(cmdPrvHex, 16) ^ (byte)Convert.ToInt32(cmdHex, 16);
                } else {
                    CRCCode = (byte)CRCCode ^ (byte)Convert.ToInt32(cmdHex, 16);
                }
            }
            return Convert.ToString(CRCCode, 16).ToUpper();//返回16进制校验码
        }

        /// <summary>
        /// 5点3次平滑曲线
        /// </summary>
        /// <param name="a">一维输入信号</param>
        /// <param name="m">阶数，阶数越高，滤波后的信号越平滑但耗时也更长</param>
        /// <returns></returns>
        public static double[] Mean5_3(double[] a, int m = 1) {
            if (a.Length < 6) return a;
            double[] b = new double[a.Length];
            int n = a.Length;
            for (int k = 0; k < m; k++) {
                b[0] = (69 * a[0] + 4 * (a[1] + a[3]) - 6 * a[2] - a[4]) / 70;
                b[1] = (2 * (a[0] + a[4]) + 27 * a[1] + 12 * a[2] - 8 * a[3]) / 35;
                for (int j = 2; j < n - 2; j++) {
                    b[j] = (-3 * (a[j - 2] + a[j + 2]) + 12 * (a[j - 1] + a[j + 1]) + 17 * a[j]) / 35;
                }
                b[n - 2] = (2 * (a[n - 1] + a[n - 5]) + 27 * a[n - 2] + 12 * a[n - 3] - 8 * a[n - 4]) / 35;
                b[n - 1] = (69 * a[n - 1] + 4 * (a[n - 2] + a[n - 4]) - 6 * a[n - 3] - a[n - 5]) / 70;
                a = b;
            }
            return b;
        }

        public static double[] kspt(double[] y) {
            var n = y.Length;
            double[] yy = new double[y.Length];
            if (n < 5) {
                for (int i = 0; i <= n - 1; i++)
                    yy[i] = y[i];
            } else {
                yy[0] = 69.0 * y[0] + 4.0 * y[1] - 6.0 * y[2] + 4.0 * y[3] - y[4];
                yy[0] = yy[0] / 70.0;
                yy[1] = 2.0 * y[0] + 27.0 * y[1] + 12.0 * y[2] - 8.0 * y[3];
                yy[1] = (yy[1] + 2.0 * y[4]) / 35.0;
                for (int i = 2; i <= n - 3; i++) {
                    yy[i] = -3.0 * y[i - 2] + 12.0 * y[i - 1] + 17.0 * y[i];
                    yy[i] = (yy[i] + 12.0 * y[i + 1] - 3.0 * y[i + 2]) / 35.0;
                }
                yy[n - 2] = 2.0 * y[n - 5] - 8.0 * y[n - 4] + 12.0 * y[n - 3];
                yy[n - 2] = (yy[n - 2] + 27.0 * y[n - 2] + 2.0 * y[n - 1]) / 35.0;
                yy[n - 1] = -y[n - 5] + 4.0 * y[n - 4] - 6.0 * y[n - 3];
                yy[n - 1] = (yy[n - 1] + 4.0 * y[n - 2] + 69.0 * y[n - 1]) / 70.0;
            }
            return yy;
        }

        /// <summary>
        /// 二次指数平滑法求预测值
        /// </summary>
        /// <param name="list">基础数据集合</param>
        /// <param name="year">未来第几期</param>
        /// <param name="modulus">平滑系数</param>
        /// <returns></returns>
        public static double GetExpect(List<double> list, int year, double modulus) {
            if (list.Count() < 10 || modulus <= 0 || modulus >= 1) {
                return 0;
            }
            double modulusLeft = 1 - modulus;
            double lastIndex = list[0];
            double lastSecIndex = list[0];
            foreach (double data in list) {
                lastIndex = modulus * data + modulusLeft * lastIndex;
                lastSecIndex = modulus * lastIndex + modulusLeft * lastSecIndex;
            }
            double a = (2 * lastIndex) - lastSecIndex;
            double b = modulus / modulusLeft * (lastIndex - lastSecIndex);
            return a + (b * year);
        }

        /// <summary>
        /// 二次指数平滑预测方法
        /// </summary>
        /// <param name="data">真实数据</param>
        /// <param name="t">预测期数</param>
        /// <param name="a">平滑系数</param>
        /// <returns></returns>
        public static double GetPrediction(List<double> data, int t, double a) {
            List<double> S1 = new List<double>();//存放第一次预测值
            List<double> S2 = new List<double>();//存放第二次预测值
            double st = 0.0;//初始预测值
            double at = 0.0;
            double bt = 0.0;
            double xt = 0.0;//本期预测值
            int size = data.Count();//实际数据总数          
            if (size < 20) {
                double s1 = data[0] + data[1] + data[2];//真实值前三项和
                st = s1 / 3;
            } else {
                st = data[0];
            }
            //第一次预测
            for (int k = 0; k < size; k++) {
                if (k == 0)
                    S1.Add((a * data[k]) + ((1 - a) * st));
                else
                    S1.Add((a * data[k]) + ((1 - a) * S1[k - 1]));
            }
            //第二次预测
            for (int l = 0; l < size; l++) {
                if (l == 0)
                    S2.Add((a * S1[l]) + ((1 - a) * st));
                else
                    S2.Add((a * S1[l]) + ((1 - a) * S2[l - 1]));
            }
            at = (S1[S1.Count() - 1] * 2) - S2[S2.Count() - 1];
            bt = a / (1 - a) * (S1[S1.Count() - 1] - S2[S2.Count() - 1]);
            xt = at + (bt * t);
            return xt;
        }

        /// <summary>
        /// 多项式预测数据
        /// </summary>
        /// <param name="fit">多项式</param>
        /// <param name="count">预测多少个数据</param>
        /// <returns></returns>
        public static List<double> GetPrediction(double[] fit, int count) {
            List<double> result = new List<double>();
            for (int i = 1; i < count + 1; i++) {
                double tmp = 0;
                for (int j = 0; j < fit.Length; j++) {
                    tmp += fit[j] * Math.Pow(i, j);
                }
                result.Add(tmp);
            }
            return result;
        }

        /// <summary>
        /// 计算标准差
        /// </summary>
        /// <param name="X"></param>
        /// <returns></returns>
        public static double StandardDeviation(IEnumerable<double> values) {
            double ret = 0;
            if (values.Count() > 0) {
                //  计算平均数   
                double avg = values.Average();
                //  计算各数值与平均数的差值的平方，然后求和 
                double sum = values.Sum(d => Math.Pow(d - avg, 2));
                //  除以数量，然后开方
                ret = Math.Sqrt(sum / values.Count());
            }
            return ret;
        }

        /// <summary>
        /// 计算方差
        /// </summary>
        /// <param name="X"></param>
        /// <returns></returns>
        public static double Variance(double[] X) {
            double avg = X.Average();
            double sum = 0;
            foreach (var x in X) {
                sum += Math.Pow(x - avg, 2);
            }
            return sum / X.Length;
        }

        /// <summary>
        /// 是否有突刺
        /// </summary>
        /// <param name="list"></param>
        /// <param name="fluctuate">波动值</param>
        /// <returns></returns>
        public static bool ExistsSpike(List<double> list, double fluctuate) {
            var decline = false;
            for (int i = 1; i < list.Count(); i++) {
                if (list[i] < list[i - 1]) {
                    decline = true;
                }
            }
            //有下降
            if (decline) {
                //波动范围
                for (int i = 1; i < list.Count(); i++) {
                    if (Math.Abs(list[i] - list[i - 1]) > fluctuate) {
                        return true;
                    }
                }
            }
            return false;
        }

        public static List<double> GoOneDeal(List<double> list) {
            var max = list.Max();
            var min = list.Min();
            List<double> temp = new List<double>();
            foreach (var item in list) {
                var value = (item - min) / (max - min);
                temp.Add(value);
            }
            return temp;
        }

        public static List<double> GoOneDeal_Zscore(List<double> list) {
            var avg = list.Average();
            double std_dev;
            STDEV(list.ToArray(), out std_dev, out _);
            List<double> temp = new List<double>();
            foreach (var item in list) {
                var value = (item - avg) / std_dev;
                temp.Add(value);
            }
            return temp;
        }


        /// <summary>
        /// 样本标准差和总体标准差计算
        /// </summary>
        /// <param name="arrData">数据数组</param>
        /// <param name="std_dev">样本标准差</param>
        /// <param name="STDP">总体标准差</param>
        public static void STDEV(double[] arrData, out double std_dev, out double STDP) //计算标准偏差
        {
            double xSum = 0;//样本总和
            double xAvg = 0;//样本平均值
            double sSum = 0;//方差的分子
            //float tmpStDev = 0F;
            int arrNum = arrData.Length;//得到样本数量，分母
            for (int i = 0; i < arrNum; i++)//循环计算得到样本总和
            {
                xSum += arrData[i];
            }
            xAvg = xSum / arrNum;//计算得到样本平均值
            for (int j = 0; j < arrNum; j++)//得到方差的分子
            {
                sSum += (arrData[j] - xAvg) * (arrData[j] - xAvg);
            }
            std_dev = Convert.ToDouble(Math.Sqrt(sSum / (arrNum - 1)).ToString());//样本标准差

            STDP = Convert.ToDouble(Math.Sqrt(sSum / arrNum).ToString());//总体标准差
        }

        /// <summary>
        /// 获取集合中数据上升的比例
        /// </summary>
        /// <param name="list"></param>
        public static double AscRate(List<double> list) {
            int asc = 0;
            int count = list.Count;
            for (int i = 1; i < count; i++) {
                if (list[i] >= list[i - 1]) {
                    asc++;
                }
            }
            return asc * 1.0 / count;
        }

        /// <summary>
        /// 获取集合中数据上升的比例
        /// </summary>
        /// <param name="list"></param>
        public static double DescRate(List<double> list) {
            int desc = 0;
            int count = list.Count;
            for (int i = 1; i < count; i++) {
                if (list[i] <= list[i - 1]) {
                    desc++;
                }
            }
            return desc * 1.0 / count;
        }
    }
}
